// #ifndef 自定义的宏名称
// #define 自定义的宏名称
// ...整个文件的内容，或某段代码...
// #endif

// 标记当前物理文件只会被包含一次，但如果文件有多个拷贝，也会被重复包含
#pragma once

#include <cstdio>
#include <iostream>
#include <vector>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <cstdlib>
#include <sys/wait.h>
#include "Task.hpp"
using namespace std;



//全公有
struct channel//先描述: 信道: 父进程--管道--子进程
{
    channel(int father_wfd, pid_t child_id)
    :_father_wfd(father_wfd)
    ,_child_id(child_id)
    {
        _name = string("chanel-") + to_string(_father_wfd) + string("-") + to_string(_child_id);
    }

    bool send(const int task_code)//子读父写: 父进程发送任务码给子进程
    {
        ssize_t ret_write = write(_father_wfd, &task_code, sizeof(int));
        return ret_write == -1 ? false : true;
    }

    void close()//关闭当前信道的_father_wfd
    {
        ::close(_father_wfd);//在公共域搜索, 否则只会找到当前类域的close
    }

    pid_t wait()//回收当前信道的子进程
    {
        return ::waitpid(_child_id, nullptr, 0);
    }

    int _father_wfd;
    pid_t _child_id;
    string _name;
};



class channel_list//再组织
{
public:
    channel_list()
    :_next_visit(0)
    {}

    
    //在列表中创建(一条)信道
    void insert()
    {
        //1.创建管道
        int pipefd[2];
        int ret_pipe = pipe(pipefd);
        if(ret_pipe == -1)
        {
            perror("创建管道失败\n");
            exit(errno);
        }

        //2.创建子进程
        pid_t sub_id = fork();
        if(sub_id == -1)
        {
            perror("创建子进程失败\n");
            exit(errno);
        }

        if(sub_id == 0)//子进程
        {
            //解决办法2: 关闭当前进程所有指向其他管道文件写端的文件描述符
            //_channels 是继承父亲的, 其内包含了所有其他哥哥进程对应信道, 其内有管道的写端, 但[不包含当前进程对应信道], 因为创建子进程之前, 父进程还没维护呢
            //全部关闭即可
            for(auto& c : _channels)
            {
                c.close();
            }

            //3.关闭不需要的文件描述符 -- 这里子读父写
            close(pipefd[1]);//关闭子的写端


            //子进程工作 -- [子进程没读到东西, 就会一直阻塞]
            child_work(pipefd[0]);//子的读端

            //子进程完成工作后: [自动]关闭读段并退出, 不用close和quit
            close(pipefd[0]);
            exit(1);
        }

        else//父进程
        {
            //3.关闭不需要的文件描述符 -- 这里子读父写
            close(pipefd[0]);//关闭父的读端

            //维护信道 -- 放进信道列表中
            _channels.emplace_back(pipefd[1], sub_id);//父的写端, 子id

        }
    }

    //移除一条信道
    void erase()
    {   
        // channel c = _channels[0];
        // cout << "关闭信道: " << c._name << endl;
        // //关闭当前信道的_father_wfd
        // c.close();
        // //回收当前信道的子进程
        // c.wait();
        // _channels.pop_back();

        // cout << "#####################" << endl;
        // sleep(3);

        //解决办法1: 倒着关
        channel c = _channels.back();
        cout << "关闭信道: " << c._name << endl;
        //关闭当前信道的_father_wfd
        c.close();
        //回收当前信道的子进程
        c.wait();
        _channels.pop_back();
    }

    //添加任务
    int add_task(task_func func)
    {
        return _tl.insert(func);
    }

    //选择信道
    channel& select()
    {
        channel& ret = _channels[_next_visit];
        ++_next_visit;
        _next_visit %= _channels.size();
        return ret;
    }

private:
    void child_work(int child_rfd)
    {
        while(true)
        {
            int task_code = 0;//一次读四个字节 -- 任务码
            ssize_t ret_read = read(child_rfd, &task_code, sizeof(int));//如果子进程没读到东西, 就会一直在这里[阻塞]
            if(ret_read == -1)
            {
                cout << "读取错误" << endl;
                break;
            }

            else if(ret_read == 0)//代表该管道文件的引用计数为0
            {
                cout << "文件引用计数为0, 进程即将被回收" << endl;
                break;
            }

            else//ret_read > 0
            {
                if(ret_read != sizeof(int))
                {
                    cout << "写入格式不合规, 待重新读取" << endl;
                    continue;
                }

                cout << "子进程[" << getpid() << "]读取到task_code为: " << task_code << endl;
                _tl.execute(task_code);
            }
        }

    }


private:
    vector<channel> _channels;
    task_list _tl;
    int _next_visit;//采用轮询策略选择信道
};